#include <stdio.h>
#include <stdlib.h>
#include <iterator>
#include <iostream>
#include <fstream>
#include <sstream>
#include <vector>
#include <string>
#include <ctime>
#include <cmath>
#include <limits>
#include <time.h>

#ifndef Pi
#define Pi 3.141592653589793238462643
#endif

clock_t curt;

typedef struct Option {
  char type;
  double strike;
  double bid;
  double ask;
  unsigned int year;
  unsigned int month;
  unsigned int day;
} option;

class CVSRow
{
public:
  std::string const& operator[](std::size_t index) const
  {
    return m_data[index];
  }
  std::size_t size() const
  {
    return m_data.size();
  }
  void readNextRow(std::istream& str)
  {
    std::string         line;
    std::getline(str,line);

    std::stringstream   lineStream(line);
    std::string         cell;

    m_data.clear();
    while(std::getline(lineStream,cell,','))
      {
	m_data.push_back(cell);
      }
  }
private:
  std::vector<std::string>    m_data;
};

std::istream& operator>>(std::istream& str,CVSRow& data)
{
  data.readNextRow(str);
  return str;
}

void parse_row(option &opt, CVSRow row) {

  opt.strike = atof(row[2].c_str());
  opt.bid = atof(row[4].c_str());
  opt.ask = atof(row[5].c_str());
  opt.year = atoi(row[0].c_str()) + 2000;

  if (row[1] == "Jan")
    opt.month = 1;
  else if (row[1] == "Feb")
    opt.month = 2;
  else if (row[1] == "Mar")
    opt.month = 3;
  else if (row[1] == "Apr")
    opt.month = 4;
  else if (row[1] == "May")
    opt.month = 5;
  else if (row[1] == "Jun")
    opt.month = 6;
  else if (row[1] == "Jul")
    opt.month = 7;
  else if (row[1] == "Aug")
    opt.month = 8;
  else if (row[1] == "Sep")
    opt.month = 9;
  else if (row[1] == "Oct")
    opt.month = 10;
  else if (row[1] == "Nov")
    opt.month = 11;
  else if (row[1] == "Dec")
    opt.month = 12;
  else
    opt.month = 0;


  if (opt.month == 1 || opt.month == 3 || opt.month == 5 ||
      opt.month == 7 || opt.month == 8 || opt.month == 10 || opt.month == 12)
    opt.day = 31;
  else if (opt.month == 2) {
    if (opt.year%4 == 0) {
      opt.day = 29;
    }
    else {
      opt.day = 28;
    }
  }
  else {
    opt.day = 30;
  }
}


double CND( double X )
{

  double L, K, w ;

  double const a1 = 0.31938153, a2 = -0.356563782, a3 = 1.781477937;
  double const a4 = -1.821255978, a5 = 1.330274429;

  L = fabs(X);
  K = 1.0 / (1.0 + 0.2316419 * L);
  w = 1.0 - 1.0 / sqrt(2 * Pi) * exp(-L *L / 2) * (a1 * K + a2 * K *K + a3 * pow(K,3) + a4 * pow(K,4) + a5 * pow(K,5));

  if (X < 0 ){
    w= 1.0 - w;
  }
  return w;
}

// The Black and Scholes (1973) Stock option formula
double BlackScholes(char CallPutFlag, double S, double X, double T, double r, double v)
{
  double d1, d2;


  d1=(log(S/X)+(r+v*v/2)*T)/(v*sqrt(T));
  d2=d1-v*sqrt(T);

  if(CallPutFlag == 'c')
    return S *CND(d1)-X * exp(-r*T)*CND(d2);
  else if(CallPutFlag == 'p')
    return X * exp(-r * T) * CND(-d2) - S * CND(-d1);
}

double getIV(double s, double k, double t, double r, double p)
{
  double cpTest = 0;
  double IV = 1.f;

  double upper = 1.f;
  double lower = 0.f;
  double range = fabs(lower - upper);

  bool dnword = true;

  int counter = 0;

  while(1)
    {
      cpTest = BlackScholes('c', s, k, t, r, IV);

//      printf("price : %f | cpTest : %f | IV : %f | counter : %d | upper : %f | lower : %f\n" , p, cpTest, IV, counter, upper, lower);

      if(cpTest > p)
        {  // Implied Volatility - IV  has to go down
	  upper = IV;
	  IV = (lower + upper) / 2;
        }
      else
        {
	  // Implied Volatility - IV has to go up
	  lower = IV;
	  IV = (lower + upper) / 2;
        }
      range = fabs(lower - upper);
      if( range < 0.0001)
	break;

      counter++;
    }

//  printf("final IV : %f\n", IV);

  return IV;
}

int julian(int year, int month, int day) {
  int a = (14 - month) / 12;
  int y = year + 4800 - a;
  int m = month + 12 * a - 3;
  if (year > 1582 || (year == 1582 && month > 10) || (year == 1582 && month == 10 && day >= 15))
    return day + (153 * m + 2) / 5 + 365 * y + y / 4 - y / 100 + y / 400 - 32045;
  else
    return day + (153 * m + 2) / 5 + 365 * y + y / 4 - 32083;
}


int dateDiff(option opt){
  unsigned int year1 = opt.year;
  unsigned int month1 = opt.month;
  unsigned int day1 = opt.day;

  time_t t = time(0);
  struct tm * now = localtime(&t);

  unsigned int year2 = now->tm_year+1900;
  unsigned int month2 = now->tm_mon + 1;
  unsigned int day2 = now->tm_mday;

  int julian_day1, julian_day2;
  int difference;

  julian_day1 = julian(year1, month1, day1);
  julian_day2 = julian(year2, month2, day2);

  difference = abs(julian_day2 - julian_day1);

  return difference;
}


void calOptPrice(option opt, int type) {

  // additional parameters for Brent's Method
  double time = (double)dateDiff(opt) / 365.f; // in year

  //printf("dateDiff : %d\n", dateDiff(opt));
  //printf("dateDiff in Double : %f\n", (double)dateDiff(opt));
  //printf("dateDiff in year : %f\n", (double)dateDiff(opt)/ 365.f);

  double imp_volatility;
  double opt_price;

  if (type == 1) {
    imp_volatility = getIV(28.69f, opt.strike, time, 0.0052f, (opt.ask + opt.bid)/2.f);
    opt_price = BlackScholes('c', 28.69f, opt.strike, time, 0.0052f, imp_volatility);
  }
  /*
  else {
    imp_volatility = BrentsMethodSolve('p', (opt.ask + opt.bid) / 2.f, opt.strike, 28.69f, 0.52f, time, 80.f, 1.f, 1.f);
    opt_price = BlackScholes('p', 28.69f, opt.strike, time, 0.52f, imp_volatility);
  }
  */
/*  printf("type : %d, ", type);
  printf("strike : %f, ", opt.strike);
  printf("bid : %f, ", opt.bid);
  printf("ask : %f, ", opt.ask);
  printf("spot : %f, ", 28.69f);
  printf("time : %f, ", time * 365.f);
  printf("implied volatility : %f, ", imp_volatility);
  printf("option price : %f ", opt_price);
  printf("\n");
*/
}


// stupid but just for the sake of checking correctness
void  extract_options(std::string symbol, int type){

  std::string filename;

  if (type == 1){
    filename = "../input/" + symbol + "_call.csv";
  }
  else {
    filename = "../input/" + symbol + "_put.csv";
  }

  //printf(filename.c_str());

  std::ifstream file(filename.c_str());
  std::vector<option> options;

  CVSRow              row;
  int i = 0;
  while(file >> row)
    {
      option opt;
      opt.type = type;
      parse_row(opt, row);
      options.push_back(opt);
    }

  printf("options.size at extract : %d\n", options.size());
  for(int i = 0; i < options.size(); i++) {
    calOptPrice(options[i], type);
  }
}


int main()
{
  // extract list of symbols
  std::ifstream file("symbol_list.txt");
  curt = clock();
  std::string symbol;
  std::string N;
  if (file.is_open()) {
    while(!file.eof()){

      getline(file, symbol);
      if (symbol == "") {
	continue;
      }
      // call options
      extract_options(symbol, 1);
      // put options
      //extract_options(symbol, 0);
    }
    file.close();

  }
  else std::cout << "Unable to open file 'symbol_list.txt'\n";

  printf("%f\n", (double)(clock() - curt)/CLOCKS_PER_SEC);
}


